﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using Perspective.Wpf3D.Primitives;
using Perspective.Wpf3D.Sculptors;
using Perspective.Core.Primitives;
using Perspective.Core.Wpf;

namespace Perspective.Wpf3D.Shapes
{
    /// <summary>
    /// 文字标签
    /// A 3D label element.
    /// Default radius is 1.0.
    /// </summary>
    public class Label3D : UIElement3D
    {
        #region Constant
        private static Color DefaultColor = Colors.LightGray;
        internal const double DefaultLength = 1.0;
        internal const double DefaultWidth = 1.0;
        #endregion

        #region Members
        ModelUIElement3D _labelElement = new ModelUIElement3D();
        GeometryModel3D _labelModel = new GeometryModel3D();
        #endregion

        #region Constructors

        /// <summary>
        /// Static constructor.
        /// </summary>
        public Label3D()
        {

        }

        #endregion

        #region Properties

        #region Length 长度

        /// <summary>
        /// Gets or sets the axis length.
        /// </summary>
        public double Length
        {
            get { return (double)GetValue(LengthProperty); }
            set { SetValue(LengthProperty, value); }
        }

        /// <summary>
        /// Identifies the Length dependency property.
        /// Default value is 1.0.
        /// </summary>
        public static readonly DependencyProperty LengthProperty =
            DependencyProperty.Register(
                "Length",
                typeof(double),
                typeof(Label3D),
                new PropertyMetadata(
                    Label3D.DefaultLength,
                    VisualPropertyChanged));
        #endregion

        #region Width 宽度

        /// <summary>
        /// Gets or sets the axis Width.
        /// </summary>
        public double Width
        {
            get { return (double)GetValue(WidthProperty); }
            set { SetValue(WidthProperty, value); }
        }

        /// <summary>
        /// Identifies the Width dependency property.
        /// Default value is 1.0.
        /// </summary>
        public static readonly DependencyProperty WidthProperty =
            DependencyProperty.Register(
                "Width",
                typeof(double),
                typeof(Label3D),
                new PropertyMetadata(
                    Label3D.DefaultWidth,
                    VisualPropertyChanged));
        #endregion

        #region Text 文字

        /// <summary>
        /// Gets or sets the axis Text.
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        /// <summary>
        /// Identifies the Text dependency property.
        /// Default value is string.Empty()
        /// </summary>
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(Label3D),
                new PropertyMetadata(
                    "",
                    VisualPropertyChanged));
        #endregion

        #region Background 背景颜色
        /// <summary>
        /// Gets or sets the axis Background.
        /// </summary>
        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        /// <summary>
        /// Identifies the Background dependency property.
        /// </summary>
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register(
                "Background",
                typeof(Brush),
                typeof(Label3D),
                new PropertyMetadata(
                    Brushes.LightGray,
                    VisualPropertyChanged));
        #endregion

        #region Foreground 文字颜色

        /// <summary>
        /// Gets or sets the axis Foreground.
        /// </summary>
        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }

        /// <summary>
        /// Identifies the Foreground dependency property.
        /// </summary>
        public static readonly DependencyProperty ForegroundProperty =
            DependencyProperty.Register(
                "Foreground",
                typeof(Brush),
                typeof(Label3D),
                new PropertyMetadata(
                    Brushes.Black,
                    VisualPropertyChanged));
        #endregion

        #endregion

        #region Functions

        /// <summary>
        /// Build the sculptor for the label.
        /// </summary>
        /// <param name="ad">An AxisDirection enum value.</param>
        /// <returns></returns>
        private SquareSculptor BuildLabelSculptor(double length,double width)
        {
            SquareSculptor sculptor = new SquareSculptor();
            Transform3DGroup tg = new Transform3DGroup();
            tg.Children.Add(new ScaleTransform3D(length, width, 1.0));
            tg.Children.Add(new TranslateTransform3D(0, width / 2.0, -0.001));
            sculptor.Transform(tg);
            sculptor.BuildMesh();
            sculptor.MapTexture();
            return sculptor;
        }

        /// <summary>
        /// Build the material for the label.
        /// </summary>
        /// <param name="text">An AxisDirection enum value.</param>
        ///  <param name="background">An AxisDirection enum value.</param>
        /// <param name="foreground">An AxisDirection enum value.</param>
        /// <returns></returns>
        private static Material BuildLabelMaterial(string text, Brush background,Brush foreground)
        {
            MaterialGroup mg = new MaterialGroup();
            mg.Children.Add(new DiffuseMaterial(background));
            TextBlock tb = new TextBlock();
            tb.FontFamily = new FontFamily("Verdana");
            tb.HorizontalAlignment = HorizontalAlignment.Center;
            tb.VerticalAlignment = VerticalAlignment.Center;
            tb.TextAlignment = TextAlignment.Center;
            tb.Foreground = foreground;
            VisualBrush vb = new VisualBrush();
            vb.Visual = tb;
            vb.Stretch = Stretch.Uniform;
            tb.Text = text;
            mg.Children.Add(new DiffuseMaterial(vb));
            return mg;
        }

        /// <summary>
        /// Called by UIElement3D.InvalidateModel() to update the 3D model.
        /// </summary>
        protected override void OnUpdateModel()
        {
            SquareSculptor labelSculptor = BuildLabelSculptor(Length, Width);
            Material labelMaterial = BuildLabelMaterial(Text,Background,Foreground);
            _labelModel.Geometry = labelSculptor.Mesh;
            _labelModel.Material = labelMaterial;
            _labelModel.BackMaterial = labelMaterial;
            _labelElement.Model = _labelModel;
            this.AddVisual3DChild(_labelElement);
            base.OnUpdateModel();
        }
          /// <summary>
        /// Callback called when a visual dependency property value has changed (i.e. by databinding).
        /// </summary>
        /// <param name="d">Sender object</param>
        /// <param name="e">Callback arguments</param>
        protected static void VisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Label3D element = (Label3D)d;
            element.InvalidateModel();
        }

        #endregion

        /// <summary>
        /// Overrides Visual3D.GetVisual3DChild(int index)
        /// </summary>
        protected override Visual3D GetVisual3DChild(int index)
        {
            Visual3D v = null;
            v = _labelElement;
            return v;
        }

        /// <summary>
        /// Overrides Visual3D.Visual3DChildrenCount
        /// </summary>
        protected override int Visual3DChildrenCount
        {
            get
            {
                return 1 ;
            }
        }
    }
}
